package com.mulesoft.service;

import java.util.HashSet;
import java.util.Set;

import com.mulesoft.domain.analyzer.MinimumWaysWithDistanceCalculator;
import com.mulesoft.domain.analyzer.WayWithDistanceSmallerToLimitCalculator;
import com.mulesoft.domain.analyzer.WaysByStepWithDistanceCalculator;
import com.mulesoft.domain.analyzer.WaysByStepsCalculator;
import com.mulesoft.domain.analyzer.MinimumWaysWithDistanceCalculator.PathInfo;

/**
 * 
 * @author iguardines
 * @version 1.0.0
 * Esta clase provee la agrupacion en un simple punto de entrada de todos los
 * calculos que se pueden realizar con los caminos.
 * Sirve como fachada para la aplicacion que quiera utilizar la biblioteca.
 */
public class PAnalyzerServiceClient {

	private static final String NO_SUCH_ROUTE = "NO SUCH ROUTE";

	public PAnalyzerServiceClient(String input) {		
		this.algorithmsInitialization(input);
	}

	private WayWithDistanceSmallerToLimitCalculator wayWithDistanceSmallerToLimitCalculator;
	private MinimumWaysWithDistanceCalculator minimumWaysWithDistanceCalculator;
	private WaysByStepsCalculator waysByStepsCalculator;
	private WaysByStepWithDistanceCalculator waysByStepWithDistanceCalculator;

	protected void algorithmsInitialization(String input){
		this.wayWithDistanceSmallerToLimitCalculator = new WayWithDistanceSmallerToLimitCalculator(input);
		this.minimumWaysWithDistanceCalculator = new MinimumWaysWithDistanceCalculator(input);
		this.waysByStepsCalculator = new WaysByStepsCalculator(input);
		this.waysByStepWithDistanceCalculator = new WaysByStepWithDistanceCalculator(input);
	}

	/** Given a valid path belonging to the graph, the method calculate if the route exists,
	 *  the distance between the dot of the route
	 * @param String route
	 * @return String representation of the distance that exist between the nodes in the route. or no such route 
	 * if there is no route.
	 */
	public String getRouteDistance(String route){
		int acumulatedDistance=0;		
		String r1= route.replace("-", "");
		char[] points = r1.toCharArray();
		
		for(int i=0; i<points.length; i++){
			if (i+1 >= points.length)break;
			
			Set<WaysByStepWithDistanceCalculator.PathInfo> pathsInfo = this.waysByStepWithDistanceCalculator.calculateWays(points[i],points[i+1],1);
			if(pathsInfo.isEmpty()){
				acumulatedDistance = 0;				
				break;
			}
			for(WaysByStepWithDistanceCalculator.PathInfo pathInfo: pathsInfo){
				acumulatedDistance += pathInfo.length;
					break;
			}

			}

		return (acumulatedDistance == 0)? NO_SUCH_ROUTE : acumulatedDistance + ""; 		
		}

    /**
     * Given a From and a To it calculates the different possibilities of do the trip with at least 
     * maxAmountOfStop
     * @param char from [A,B,..]
     * @param char to [A,B,..] 
     * @param int maxAmountOfStop
     * @return String representation of the possibles amount of trips that match with at least
     * maxAmountOfStop amount of stops or NO_SUCH_ROUTE otherwise.
     * 
     */
	public String getAmountOfTripWithMaximAmountStopOf(char from, char to, int maxAmountOfStop){
		Set<String> sols = new HashSet<String>();
		while(maxAmountOfStop !=0 ){			
			sols.addAll(waysByStepsCalculator.calculateWays(from, to, maxAmountOfStop--));
		}

		return (sols.isEmpty())? NO_SUCH_ROUTE:  sols.size() +"";
	}

	/**
	 * Given a From and a To belonging to the graph, it calculate the different possibles trips with a fixed amount
	 * of stops
     * @param char from [A,B,..]
     * @param char to [A,B,..] 
     * @param int cantidadMaxDeIntervalos
     * @return String representation of the amount of possibles different trip that match with the amountOfStops  
     * NO_SUCH_ROUTE otherwise.
     */
	public String getAmountOfTripsWithFixedAmountofStops(char from, char to,int amountOfStops){
		Set<String> sols = new HashSet<String>();
		sols.addAll(waysByStepsCalculator.calculateWays(from, to, amountOfStops));
		
		return (sols.isEmpty())? NO_SUCH_ROUTE:  sols.size() +"";
	}

	/**
	 * Given a From and a To belonging to the graph, it calculates if exists, the shortest route
	 * @param char from [A,B,..]
	 * @param char to [A,B,..]
	 * @return String representation of the length of the shortest route between 2 nodes,
	 * NO SUCH ROUTE if the route doesn't exist
	 */
	public String getTheShortestDistance(char from, char to){
		Set<PathInfo> pathsInfo = this.minimumWaysWithDistanceCalculator.calculateMinimunWays(from, to);

		for(PathInfo path:pathsInfo){
			return path.length + "";
		}
		return NO_SUCH_ROUTE;
	}

	/**
	 * Given a From and a To belonging to the graph and a limit of weight it calculates if exists all possibles ways
	 * which path connect its nodes and not overpass the weight limit.
	 * @param char From [A,B,..]
	 * @param char To [A,B,..]
	 * @return String representation of the amount of different routes found
	 * which sum of weight is within the limits, NO SUCH ROUTE otherwise.
	 */
	public String getAmountOfDifferentRoutesWithDistanceSmallerToALimit(char from, char to, int limit ){
		Set<WayWithDistanceSmallerToLimitCalculator.PathInfo> solsAux = new HashSet<WayWithDistanceSmallerToLimitCalculator.PathInfo>(); 
		Set<WayWithDistanceSmallerToLimitCalculator.PathInfo> sols = wayWithDistanceSmallerToLimitCalculator.calculateWays(from, to, limit);
		
		if(sols.isEmpty()){ 
			return NO_SUCH_ROUTE;
		}
		
		for(WayWithDistanceSmallerToLimitCalculator.PathInfo pathInfo : sols){
			if(pathInfo.length > 0 && pathInfo.length < limit ){
				solsAux.add(pathInfo);
			}
		}
		
		return solsAux.size() + "" ;
	}
}